/**
 * This file is part of Too-OneBee.
 *
 * Too-OneBee is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Too-OneBee is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Too-OneBee.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.dataforte.tooonebee.context;

import static net.dataforte.tooonebee.Menu.menu;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.Principal;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.dataforte.tooonebee.Menu;
import net.dataforte.tooonebee.ServiceIntrospector;

public class ContextIntrospector extends ServiceIntrospector {

	private static final String SYSTEM_PROPERTIES = "System Properties";
	private static final String SERVER_INFORMATION = "Server Information";
	private static final String APPLICATION_ATTRIBUTES = "Application Attributes";
	private static final String SESSION_ATTRIBUTES = "Session Attributes";
	private static final String REQUEST_ATTRIBUTES = "Request Attributes";
	private static final String REQUEST_HEADERS = "Request Headers";
	private static final String REQUEST_INFORMATION = "Request Information";
	
	@Override
	public Set<Menu> getMenuEntries() {
		Set<Menu> entries = super.getMenuEntries();
		entries.add(
			menu("Servlet Context", this.getRequestPath(),
				menu("Server", "server"),
				menu("Application", "application"),
				menu("Session", "session"),
				menu("Request", "request")
			)
		);
		return entries;
	}

	@Override
	public String getRequestPath() {
		return "context";
	}

	@Override
	public void handleRequest(HttpServlet servlet, HttpServletRequest req, HttpServletResponse res) throws Exception {
		String[] paths = req.getPathInfo().split("/");
		Map<String, Map<String,String>> attributes = null;
		if("server".equals(paths[2])) {
			attributes = getServerAttributes();
		} else if("application".equals(paths[2])) {
			attributes = getApplicationAttributes(servlet);
		} else if("session".equals(paths[2])) {
			attributes = getSessionAttributes(req.getSession());
		} else if("request".equals(paths[2])) {
			attributes = getRequestAttributes(req);
		} else {
			throw new ServletException("Unknown context "+paths[1]);
		}
		
		res.setContentType("text/xml; UTF-8");
		PrintWriter writer = res.getWriter();
		writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
		writer.println("<root>");
		
		for(Entry<String, Map<String, String>> section : attributes.entrySet()) {
			writer.printf("<section name=\"%s\">\n", section.getKey());
			for(Entry<String,String> attribute : section.getValue().entrySet()) {
				writer.printf("<attribute name=\"%s\"><![CDATA[%s]]></attribute>\n", attribute.getKey(), attribute.getValue());
			}
			writer.println("</section>");
		}
		writer.println("</root>");
	}
	
	private Map<String, Map<String,String>> getRequestAttributes(HttpServletRequest req) {
		Map<String, Map<String,String>> a = new LinkedHashMap<String, Map<String,String>>();
		
		addEntry(a, REQUEST_INFORMATION, "Auth Type", req.getAuthType());
		addEntry(a, REQUEST_INFORMATION, "Character Encoding", req.getCharacterEncoding());
		addEntry(a, REQUEST_INFORMATION, "Content Type", req.getContentType());
		addEntry(a, REQUEST_INFORMATION, "Content Length", Integer.toString(req.getContentLength()));
		addEntry(a, REQUEST_INFORMATION, "Context Path", req.getContextPath());
		addEntry(a, REQUEST_INFORMATION, "Local Address", req.getLocalAddr());
		addEntry(a, REQUEST_INFORMATION, "Local Name", req.getLocalName());
		addEntry(a, REQUEST_INFORMATION, "Local Port", Integer.toString(req.getLocalPort()));
		addEntry(a, REQUEST_INFORMATION, "Locale", req.getLocale().toString());
		addEntry(a, REQUEST_INFORMATION, "Method", req.getMethod());
		addEntry(a, REQUEST_INFORMATION, "Path Info", req.getPathInfo());
		addEntry(a, REQUEST_INFORMATION, "Remote Address", req.getRemoteAddr());
		addEntry(a, REQUEST_INFORMATION, "Remote Host", req.getRemoteHost());
		Principal userPrincipal = req.getUserPrincipal();
		addEntry(a, REQUEST_INFORMATION, "User Principal", userPrincipal!=null?userPrincipal.toString():"null");
		addEntry(a, REQUEST_INFORMATION, "Remote User", req.getRemoteUser());
		addEntry(a, REQUEST_INFORMATION, "Request URI", req.getRequestURI());
		addEntry(a, REQUEST_INFORMATION, "Request URL", req.getRequestURL().toString());
		addEntry(a, REQUEST_INFORMATION, "Requested Session ID", req.getRequestedSessionId());
		addEntry(a, REQUEST_INFORMATION, "Secure", Boolean.toString(req.isSecure()));
		addEntry(a, REQUEST_INFORMATION, "Server Port", Integer.toString(req.getServerPort()));
		
		for(Enumeration<?> e=req.getHeaderNames(); e.hasMoreElements(); ) {
			String name = (String) e.nextElement();
			addEntry(a, REQUEST_HEADERS, name, req.getHeader(name));
		}
		
		for(Enumeration<?> e = req.getAttributeNames(); e.hasMoreElements(); ) {
			String name = (String) e.nextElement();
			Object value = req.getAttribute(name);
			addEntry(a, REQUEST_ATTRIBUTES, name, value!=null?value.toString():"null");
		}
		
		return a;
	}

	private Map<String, Map<String,String>> getSessionAttributes(HttpSession session) {
		Map<String, Map<String,String>> a = new LinkedHashMap<String, Map<String,String>>();
		
		for(Enumeration<?> e = session.getAttributeNames(); e.hasMoreElements(); ) {
			String name = (String) e.nextElement();
			Object value = session.getAttribute(name);
			addEntry(a, SESSION_ATTRIBUTES, name, value!=null?value.toString():"null");
		}
		return a;
	}

	private Map<String, Map<String,String>> getApplicationAttributes(HttpServlet servlet) {
		Map<String, Map<String,String>> a = new LinkedHashMap<String, Map<String,String>>();
		
		ServletContext servletContext = servlet.getServletContext();
		for(Enumeration<?> e = servletContext.getAttributeNames(); e.hasMoreElements(); ) {
			String name = (String) e.nextElement();
			Object value = servletContext.getAttribute(name);
			addEntry(a, APPLICATION_ATTRIBUTES, name, value!=null?value.toString():"null");
		}
		return a;
	}

	private Map<String, Map<String,String>> getServerAttributes() throws IOException {
		Map<String, Map<String,String>> a = new LinkedHashMap<String, Map<String,String>>();
		
		addEntry(a, SERVER_INFORMATION, "Hostname", java.net.InetAddress.getLocalHost().getHostName());
		
		for(Enumeration<Object> e=System.getProperties().keys(); e.hasMoreElements(); ) {
			String key = (String) e.nextElement();
			addEntry(a, SYSTEM_PROPERTIES, key, System.getProperty(key));
		}
		return a;
	}

	private static void addEntry(Map<String, Map<String,String>> map, String sectionName, String attributeName, String attributeValue) {
		Map<String, String> section = map.get(sectionName);
		
		if(section==null) {
			section = new LinkedHashMap<String, String>();
			map.put(sectionName, section);
		}
		
		section.put(attributeName, attributeValue);
	}
}
